﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Net.Sockets;
using System.Threading;
using System.Windows;

namespace ChatClientWPFVersion.Utility
{
    internal class MessageStream
    {

        private TcpClient _tcpClient;
        private bool _running;
        private Queue<string> _messages;
        private StreamReader _reader;
        private StreamWriter _writer;

        private readonly ConnectionWorker _connectionWorker;

        public delegate void MessageResponseDelegate(string message);

        public event MessageResponseDelegate HandleResponse;

        public MessageStream()
        {
            _connectionWorker = new ConnectionWorker(this);
        }

        public void Start()
        {
            if (_running) return;

            System.Diagnostics.Debug.WriteLine("Starting messages stream...");
            _messages = new Queue<string>();

            try
            {
                _tcpClient = new TcpClient(Properties.Settings.Default.IPAddress, Properties.Settings.Default.PortNumber);
                _reader = new StreamReader(_tcpClient.GetStream());
                _writer = new StreamWriter(_tcpClient.GetStream());

                _connectionWorker.RunWorkerAsync();
            }
            catch (SocketException e)
            {
                System.Diagnostics.Debug.WriteLine("Start error: " + e);
                MessageBox.Show(@"Error: Cannot connect to server");
            }
        }

        public void SendMessage(string message)
        {
            lock (_messages)
            {
                _messages.Enqueue(message);
            }
        }

        public void Close()
        {
            if (!_running) return;

            _writer.WriteLine(("END"));
            _writer.Flush();
            _connectionWorker.CancelAsync();
            _tcpClient.Close();

            _running = false;
        }

        private class ConnectionWorker : BackgroundWorker
        {
            private readonly MessageStream _outer;

            public ConnectionWorker(MessageStream outer)
            {
                _outer = outer;
                WorkerReportsProgress = false;
                WorkerSupportsCancellation = true;
            }

            protected override void OnDoWork(DoWorkEventArgs e)
            {
                _outer._running = true;
                System.Diagnostics.Debug.WriteLine("Connection loop running....");

                try
                {
                    while (_outer._running && !CancellationPending)
                    {
                        //Application.DoEvents();

                        BackgroundWorker worker = new BackgroundWorker();

                        worker.DoWork += delegate(object s, DoWorkEventArgs args)
                                             {

                                                 lock (_outer._messages)
                                                 {
                                                     System.Diagnostics.Debug.WriteLine("Message queue size: " +
                                                                                        _outer._messages.Count);

                                                     if (_outer._messages.Count == 0)
                                                     {
                                                         System.Diagnostics.Debug.WriteLine("No messages to send....");
                                                         Thread.Sleep(100);
                                                     }
                                                     else
                                                     {
                                                         var message = _outer._messages.Dequeue();
                                                         System.Diagnostics.Debug.WriteLine("Sending: " + message);
                                                         _outer._writer.WriteLine(message);
                                                         _outer._writer.Flush();

                                                         System.Diagnostics.Debug.WriteLine("Waiting for response...");
                                                         var response = _outer._reader.ReadLine();

                                                         System.Diagnostics.Debug.WriteLine("Got response: " + response);
                                                         _outer.HandleResponse(response);

                                                         worker.RunWorkerAsync();
                                                     }
                                                 }
                                             };
                    }
                }
                catch (Exception error)
                {
                    System.Diagnostics.Debug.WriteLine("Connection loop terminated" + error);
                    _outer.Close();
                }
            }
        }
    }
}